iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1
自我挑戰組

非本科之30天Ruby / Rails學習筆記系列 第 9

Day9: Ruby的三種存取控制Public, Private, Protected

  • 分享至 

  • xImage
  •  


圖片來源

前面有提過,Ruby是一門相當徹底的物件導向語言,幾乎所有的東西都是物件,但block除外。要改變物件的狀態,在ruby最常見的方式就是.method,要拿這個method來用,就要從class內來存取,這個行為就是「存取控制」。

物件(object) = 狀態(state) + 行為(behavior)

來看最簡單的例子:

class Disney
  def feel
    puts "I feel very happy"
  end
end

you = Disney.new
you.feel # => I feel very happy

you.feel這個東西看起來沒什麼特別,但在Ruby的解讀,會像有一個you物件,對它發送了一個「訊息」(message)feel,而這個you就是訊息的「接收者」(receiver)。

一般來說,public的存取是Ruby已預設好的。就像上面例子,如果在方法上面沒有特別寫是哪種存取方式的話,那就是會是預設的public

用程式碼來看看三種控制:

class Disney
  def feel
    puts "I feel very happy"
  end

  private
  def eat
    puts "I wanna have some popcorn"
  end

  protected
  def watch
    puts "I wanna watch the show"
  end
end

you = Disney.new
you.feel   # => I feel very happy
you.eat    # => NoMethodError
you.watch  # => NoMethodError

另一種存取控制的寫法,寫在定義之後:

class Disney
  def feel
    puts "I feel very happy"
  end

  def eat
    puts "I wanna have some popcorn"
  end

  def watch
    puts "I wanna watch the show"
  end
  
  protected :eat
  private :watch
end

you = Disney.new
you.feel   # => I feel very happy
you.eat    # => NoMethodError
you.watch  # => NoMethodError

至於兩種寫法其實並沒有差別,我自己是比較偏向第一種方式,因為第二種感覺比較容易跟rails搞混,例如:

has_many :users

Private

在各個程式語言都有存取控制的設計,private在其他語言被定義為「只有在類別內部才能被存取」,但在Ruby這裡的定義有點不太一樣:

不能在外部呼叫,也不能明確的指出「接收者」(receiver)

翻譯蒟蒻:若要呼叫private的方法時,前面不可以加小數點。
(備註:外部的定義不包含繼承的子類別)

上面例子因為eat方法是private method,所以向you(receiver)發送eat訊息(message)會出現NoMethodError。

但private真的那麼private嗎?

如果定義上不能在receiver前加小數點,那我改成其他形式總行吧?

you = Disney.new
you.feel   # => I feel very happy
you.send(:eat) # => I wanna have some popcorn

send是Ruby內建的方法,這邊只是把:eat當作參數送給you而已,指出接收者的是send方法而不是private method的eat,並沒有違反"不能明確的指出「接收者」的規定",所以這也是Ruby跟其他程式語言在private定義不同的地方。

Protected

class Disney
  def feel
    puts "I feel very happy"
  end

  private
  def eat
    puts "I wanna have some popcorn"
  end

  protected
  def watch
    puts "I wanna watch the show"
  end
end

class Marvel < Disney
  def marvel_watch
    watch
  end

  def marvel_self_watch
    self.watch
  end
end

iron = Marvel.new
iron.marvel_watch       # => I wanna watch the show
iron.marvel_self_watch  # => I wanna watch the show

以繼承的類別Marvel來看,在父類別的protected method,不管用的是watchself.watch都可以順利印出來。

不能在外部呼叫,但可以明確的指出「接收者」(receiver)

那如果改用private method呢?

class Disney
  def feel
    puts "I feel very happy"
  end

  private
  def eat
    puts "I wanna have some popcorn"
  end

  protected
  def watch
    puts "I wanna watch the show"
  end
end

class Marvel < Disney
  def marvel_eat
    eat
  end

  def marvel_self_eat
    self.eat
  end
end

iron = Marvel.new
iron.marvel_eat       # => I wanna have some popcorn
iron.marvel_self_eat  # => NoMethodError

最後一行會印出錯誤,是因為private method不能明確的指出「接收者」。

總結

  1. 三種方法都可以在類別內部呼叫。
  2. private:不能在外部呼叫,也不能明確的指出「接收者」(receiver)。
  3. protected:不能在外部呼叫,但可以明確的指出「接收者」(receiver)。

參考資料:
Ruby Access Control Basics: Public vs Private vs Protected methods
Ruby Access Control
類別(Class)與模組(Module)
Ruby三種存取限制: Public, Protected, Private
Ruby: public, protected 與 private

“Success is falling nine times and getting up ten.”

— Jon Bon Jovi, Musician

本文同步發佈於: https://louiswuyj.tw/


上一篇
Day8: Module的引入include和extend
下一篇
Day10: Ruby中的Conditional Assignment Operator: x ||= y(or-equals)
系列文
非本科之30天Ruby / Rails學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言